package com.zhouwd.zcamera;

import android.media.AudioFormat;
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaFormat;
import android.media.MediaMuxer;
import android.os.Environment;
import android.util.Log;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Date;

public class MediaRecordManager implements Runnable{

    private static final String TAG = MediaRecordManager.class.getSimpleName();

    private String AUDIO_MIME_TYPE="audio/mp4a-latm";
    private int AUDIO_KEY_CHANNEL_COUNT = 1;
    private int AUDIO_KEY_SAMPLE_RATE = 44100;
    private int AUDIO_KEY_BIT_RATE = 64000;
    private int AUDIO_KEY_AAC_PROFILE = MediaCodecInfo.CodecProfileLevel.AACObjectLC;

    private int AUDIO_FORMAT = AudioFormat.ENCODING_PCM_16BIT;
    private int CHANNEL_MODE = AudioFormat.CHANNEL_IN_STEREO;


    private String VIDEO_MIME_TYPE = "video/avc";
    private int VIDEO_KEY_RATE = 3500000;
    private int VIDEO_KEY_FRAME_RATE = 30;
    private int VIDEO_KEY_FRAME_INTERVAL = 2;
    private int VIDEO_KEY_WIDTH = 720;
    private int VIDEO_KEY_HEIGHT = 1280;
    private MediaCodec mVideoCodec;
    private long videoPresentationTimeUs;
    private long audioPresentationTimeUs;
    private int audioTrack;
    private int videoTrack;
    private MediaCodec.BufferInfo ebi;
    private MediaCodec.BufferInfo abi;
    private MediaCodec mAudioEncoder;
    private MediaMuxer mMediaMuxer;
    private boolean isStart = false;
    public MediaRecordManager() {
        try {
            initMediaMuxer();
        } catch (IOException e) {
            e.printStackTrace();
        }

        try {
            initAudioEncode();
        } catch (IOException e) {
            e.printStackTrace();
        }

        try {
            initVideoEncode();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    private void initMediaMuxer() throws IOException {

        String filePath = Environment.getExternalStorageDirectory()+ File.separator + "zhouwd" + File.separator + System.currentTimeMillis() +  ".mp4";

        mMediaMuxer = new MediaMuxer(filePath, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);

    }


    private void initAudioEncode() throws IOException {

        mAudioEncoder = MediaCodec.createEncoderByType(AUDIO_MIME_TYPE);
        MediaFormat audioMediaFormat = MediaFormat.createAudioFormat(AUDIO_MIME_TYPE, AUDIO_KEY_BIT_RATE, AUDIO_KEY_CHANNEL_COUNT);
        audioMediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, AUDIO_KEY_BIT_RATE);
        audioMediaFormat.setInteger(MediaFormat.KEY_AAC_PROFILE, AUDIO_KEY_AAC_PROFILE);
        mAudioEncoder.configure(audioMediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);

        abi = new MediaCodec.BufferInfo();
        audioTrack = mMediaMuxer.addTrack(audioMediaFormat);
    }


    private void initVideoEncode() throws IOException {

        mVideoCodec = MediaCodec.createEncoderByType("video/avc");

        MediaFormat format = MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_AVC, VIDEO_KEY_WIDTH, VIDEO_KEY_HEIGHT);
        format.setInteger(MediaFormat.KEY_BIT_RATE, VIDEO_KEY_RATE);
        format.setInteger(MediaFormat.KEY_FRAME_RATE, VIDEO_KEY_FRAME_RATE);
        format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, VIDEO_KEY_FRAME_INTERVAL);
        format.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible);
        videoPresentationTimeUs = new Date().getTime() * 1000;
        ebi = new MediaCodec.BufferInfo();
        mVideoCodec.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
        videoTrack = mMediaMuxer.addTrack(format);
    }

    /**
     * 编码视频
     * @param data
     */
    public void encodeVideo(byte[] data) {

        if (!isStart) {
            return;
        }

        Log.i(TAG, "encode: ");
        int index = mVideoCodec.dequeueInputBuffer(-1);
        Log.i(TAG, "encode: index -->" + index);
        if (index >= 0) {

            ByteBuffer buffer = mVideoCodec.getInputBuffer(index);

            buffer.clear();
            buffer.put(data);

            long pts = new Date().getTime() * 1000 - videoPresentationTimeUs;

            mVideoCodec.queueInputBuffer(index, 0 , data.length, pts, 0);
        }

    }

    /**
     * 编码音频
     * @param data
     */
    public void encodeAudio(byte[] data) {

        if (!isStart) {
            return;
        }

        Log.i(TAG, "encode: ");
        int index = mAudioEncoder.dequeueInputBuffer(-1);
        Log.i(TAG, "encode: index -->" + index);
        if (index >= 0) {

            ByteBuffer buffer = mAudioEncoder.getInputBuffer(index);

            buffer.clear();
            buffer.put(data);

            long pts = new Date().getTime() * 1000 - audioPresentationTimeUs;

            mAudioEncoder.queueInputBuffer(index, 0 , data.length, pts, 0);
        }



    }

    /**
     * 开始录制
     */
    public void start() {

        mAudioEncoder.start();
        mVideoCodec.start();
        mMediaMuxer.start();

        if (!isStart) {
            isStart = true;
        }

        new Thread(this).start();

    }

    /**
     * 停止录制
     */
    public void stop() {
        mAudioEncoder.stop();
        mVideoCodec.stop();
        mMediaMuxer.stop();

        if (isStart) {
            isStart = false;
        }
    }


    public void release() {
        mAudioEncoder.release();
        mVideoCodec.release();
        mMediaMuxer.release();

    }

    private void drainVideo() {

        int outBufferIndex = mVideoCodec.dequeueOutputBuffer(ebi, 0);
        Log.i(TAG, String.format("try to dequeue output buffer, oi=%d", outBufferIndex));
        if (outBufferIndex >= 0) {
            ByteBuffer bb = mVideoCodec.getOutputBuffer(outBufferIndex);
                /*byte[] outData = new byte[ebi.size];

                bb.get(outData);*/
            if (bb != null)
                mMediaMuxer.writeSampleData(videoTrack, bb, ebi);

                Log.i(TAG, "encode: bb --> " + ebi.size);

            //onEncodedAnnexbFrame(bb, ebi);
            mVideoCodec.releaseOutputBuffer(outBufferIndex, false);
        }

    }

    private void drainAudio() {

        int outBufferIndex = mAudioEncoder.dequeueOutputBuffer(abi, 0);
        Log.i(TAG, String.format("try to dequeue output buffer, oi=%d", outBufferIndex));
        if (outBufferIndex >= 0) {
            ByteBuffer bb = mAudioEncoder.getOutputBuffer(outBufferIndex);
                /*byte[] outData = new byte[ebi.size];

                bb.get(outData);*/
            if (bb != null)
                mMediaMuxer.writeSampleData(audioTrack, bb, abi);

                Log.i(TAG, "encode: bb --> " + abi.size);

            //onEncodedAnnexbFrame(bb, ebi);
            mAudioEncoder.releaseOutputBuffer(outBufferIndex, false);
        }
    }

    @Override
    public void run() {

        while (isStart) {

            drainVideo();
            drainAudio();
        }

    }
}
